#include "hiddevice.h"
#include "dmcomm.h"
#include <thread>

std::vector<DevInfo> HIDDevice::getDeviceInfo()
{
    std::vector<DevInfo> vect;
    auto dev = hid_enumerate(0x2E3C, 0xAF01);
    DevInfo info;
    info.type = DevType::HID;
    while (dev) {
        info.path = dev->path;
        info.id = ntos<unsigned short>(dev->vendor_id);
        info.desc = ntos<unsigned short>(dev->product_id);
        vect.push_back(info);
        dev = dev->next;
    }
    hid_free_enumeration(dev);
    return vect;
}

HIDDevice::HIDDevice(const std::string &path)
    : DeviceInterface{path}
{

}

HIDDevice::~HIDDevice()
{
    if (m_dev) {
        hid_close(m_dev);
        m_dev = nullptr;
    }
}

bool HIDDevice::open()
{
    m_dev = hid_open_path(m_path.c_str());
    if (!m_dev) {
        errorOccurred(DmCommError::OpenError);
        return false;
    }
    hid_set_nonblocking(m_dev, 1);
    return true;
}

void HIDDevice::close()
{
    if (m_dev) {
        hid_close(m_dev);
        m_dev = nullptr;
    }
}

bool HIDDevice::isOpen() const
{
    return m_dev;
}

void HIDDevice::write(const std::vector<uint8_t> &data, const uint16_t &timeout)
{
    if (!m_dev || data.empty()) {
        return;
    }
    constexpr size_t max_single_data = 63;
    constexpr size_t total_size = max_single_data + 1;
    size_t no_sent_count = 0;
    size_t data_offset = 0;
    std::vector<uint8_t> report;
    report.reserve(total_size);

    while (data_offset < data.size()) {
        report.clear();
        report.push_back(0x00);

        const size_t remaining = data.size() - data_offset;
        const size_t send_len = std::min(remaining, max_single_data);

        report.insert(report.end(), data.begin() + data_offset, data.begin() + data_offset + send_len);

        const int res = hid_write(m_dev, report.data(), report.size());
        if (res < 0) {
            errorOccurred(DmCommError::WriteError);
            return;
        }
        if (res <= 1) {
            ++no_sent_count;
            if (no_sent_count > 5) {
                errorOccurred(DmCommError::WriteError);
                return;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            continue;
        }
        data_offset += static_cast<size_t>(res) - 1;
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }

    m_data.clear();
    for (int i = 0; i < 128; ++i) {
        auto size = hid_read_timeout(m_dev, m_buf, READ_BUFFER_SIZE, i > 0 ? READ_TIMEOUT_MS : timeout);

        if (size < 0) {
            errorOccurred(DmCommError::ReadError);
            return;
        } else if (size == 0) {
            break;
        }

        m_data.insert(m_data.end(), m_buf, m_buf + size);

    }
    if (m_data.empty()) {
        return;
    }
    recv(m_data);
    m_data.clear();
}

std::string HIDDevice::getDescription() const
{
    return hid_get_device_info(m_dev)->path;
}

void HIDDevice::errorOccurred(int e)
{
    m_dev = nullptr;
    error(e);
}


